﻿using System.Reflection;
using System.Runtime.Loader;

namespace PluginConsoleApp
{
    internal class LazyPluginLoadContext : CollectibleAssemblyLoadContext, IPluginContext
    {
        private AssemblyDependencyResolver _resolver;

        public LazyPluginLoadContext(string pluginId, string pluginMainDllFilePath)
            : base(name: pluginId)
        {
            _resolver = new AssemblyDependencyResolver(pluginMainDllFilePath);
        }

        protected override Assembly? Load(AssemblyName name)
        {
            // 1. 先尝试 从本插件文件夹中搜索
            var assemblyPath = this._resolver.ResolveAssemblyToPath(name);
            if (!string.IsNullOrEmpty(assemblyPath))
            {
                /*
                 * return LoadFromAssemblyPath(assemblyPath);
                 * 会锁定dll
                 * 锁定dll 会导致: 1. 无法通过复制粘贴替换 更新 2. 无法删除
                 */

                // 使用此方法, 就不会导致dll被锁定
                using var fs = new FileStream(assemblyPath, FileMode.Open);
                return base.LoadFromStream(fs);
            }

            // 2. 再尝试 从其他启用的插件文件夹中搜索
            // 实测: 可在下方搜索, 主程序包括的 assemblyName 与 启用插件加载的 assemblyName 都会位于其中
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            var temp = assemblies.FirstOrDefault(m => m.GetName().FullName == name.FullName);
            if (temp != null)
            {
                return temp;
            }

            // 3. 最后搜索不到, 返回 null, 即代表使用主程序提供, 如果最后几次都为 null, 则会报错
            // 当启用本插件/触碰到本插件中的一些类型时, 而当主程序中没有提供相关的此 assemblyName 时, 也会触发此方法 来尝试加载

            return default;
        }

        protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
        {
            var path = this._resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
            if (!string.IsNullOrEmpty(path))
            {
                return base.LoadUnmanagedDllFromPath(path);
            }

            return IntPtr.Zero;
        }
    }
}
